﻿#include "../ctlImage.h"
#include "../x/draw/draw.h"

namespace swui
{
	void ctlImage::preProcessMatrix()
	{
		if(_fail || !_image) return;
		Point pt[4] = { Point(0, 0), Point(getWidth(), 0), 
			Point(0, getHeight()), Point(getWidth(), getHeight()) };
		getNativeMatrix()->TransformPoints(pt, 4);
		int maxx = pt[0].X, maxy = pt[0].Y;
		for(int i = 1; i != 4; ++i)
		{
			if(pt[i].X > maxx) maxx = pt[i].X;
			if(pt[i].Y > maxy) maxy = pt[i].Y;
		}
		Bitmap* bmp = new Bitmap(maxx + 2, 
			maxy + 2, PixelFormat32bppARGB);
		RectF dest(0, 0, (float)getWidth(), (float)getHeight());
		Bitmap* img = reinterpret_cast<Bitmap*>(_image);
		Graphics graphics(bmp);
		graphics.SetTransform(getNativeMatrix());		
		graphics.DrawImage(img, dest, 0, 0, 
			(float)getWidth(), (float)getHeight(), UnitPixel);
		_image = bmp;
		_matrix_enable = false;
		resetMatrix();
		delete img;
	}
	std::shared_ptr<ctlImage> ctlImage::preProcessMatrixNew()
	{
		if(_fail || !_image) return nullptr;
		Point pt[4] = { Point(0, 0), Point(getWidth(), 0), 
			Point(0, getHeight()), Point(getWidth(), getHeight()) };
		getNativeMatrix()->TransformPoints(pt, 4);
		int maxx = pt[0].X, maxy = pt[0].Y;
		for(int i = 1; i != 4; ++i)
		{
			if(pt[i].X > maxx) maxx = pt[i].X;
			if(pt[i].Y > maxy) maxy = pt[i].Y;
		}
		std::shared_ptr<ctlImage> bmp = createImage(
			maxx + 2, maxy + 2, ctlColorFormat::rgba);
		RectF dest(0, 0, (float)getWidth(), (float)getHeight());
		Bitmap* img = reinterpret_cast<Bitmap*>(_image);
		Graphics graphics(reinterpret_cast<Bitmap*>(bmp->getGdipHandle()));
		graphics.SetTransform(getNativeMatrix());		
		graphics.DrawImage(img, dest, 0, 0, 
			(float)getWidth(), (float)getHeight(), UnitPixel);
		return bmp;
	}
	ctlImage::ctlImage(unsigned w, unsigned h, ctlColorFormat color32)
	{
		_image = new Bitmap(w, h, color32 == ctlColorFormat::rgba ?
			PixelFormat32bppARGB : PixelFormat24bppRGB);
		if(reinterpret_cast<Bitmap*>(_image)->GetLastStatus() == Ok)
			_fail = false;
		else _fail = true;
		_matrix_enable = false;
		if(isColor32()) _cpdc = nullptr;
		else _cpdc = new _ctlInterior::compatibleDC(w, h);
		_stream = nullptr;
		flush();
	}
	ctlImage::ctlImage(const ctlImage& img)
	{
		_image = reinterpret_cast<Bitmap*>(img._image)->Clone(
			0, 0, img.getWidth(), img.getHeight(), 
			img.isColor24() ? PixelFormat24bppRGB : PixelFormat32bppARGB);
		_fail = false;
		_stream = nullptr;
		_matrix_enable = false;
		if(isColor32()) _cpdc = nullptr;
		else _cpdc = new _ctlInterior::compatibleDC(getWidth(), getHeight());
		flush();
	}
	ctlImage::ctlImage(void* buffer, std::size_t len)
	{
		HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, len);
		std::memcpy(GlobalLock(hMem), buffer, len);
		GlobalUnlock(hMem);
		_stream = nullptr;
		_matrix_enable = false;
		HRESULT result = CreateStreamOnHGlobal(hMem, TRUE, &_stream);
		if(result == S_OK)
		{
			_image = new Bitmap(_stream);
			if(reinterpret_cast<Bitmap*>(_image)->GetLastStatus() == Ok)
				_fail = false;
			else _fail = true;
			if(isColor32()) _cpdc = nullptr;
			else _cpdc = new _ctlInterior::compatibleDC(getWidth(), getHeight());
			flush();
		} else {
			GlobalFree(hMem);
			_fail = true;
		}
	}
	ctlImage::ctlImage(const ctlText& text)
	{
		_matrix_enable = false;
		_image = new Bitmap(text.toWideString().getWideString());
		if(reinterpret_cast<Bitmap*>(_image)->GetLastStatus() == Ok)
			_fail = false;
		else _fail = true;
		if(!isColor24()) _cpdc = nullptr;
		else _cpdc = new _ctlInterior::compatibleDC(getWidth(), getHeight());
		_stream = nullptr;
		flush();
	}
	ctlImage::~ctlImage()
	{
		if(_image) delete reinterpret_cast<Bitmap*>(_image);
		if(_cpdc) delete _cpdc;
		if(_stream) _stream->Release();
	}

	void ctlImage::flush()
	{
		if(!_fail && isColor24())
		{
			Graphics graphics(_cpdc->GetDC());
			graphics.DrawImage(reinterpret_cast<Bitmap*>(getGdipHandle()),
				Rect(0, 0, getWidth(), getHeight()), 0, 0, getWidth(), getHeight(), UnitPixel);
		}
	}
	void* ctlImage::getDC() const
	{
		return reinterpret_cast<void*>(_cpdc->GetDC());
	}
	void* ctlImage::getGdipHandle()
	{
		return reinterpret_cast<void*>(_image);
	}
	unsigned ctlImage::getHeight() const
	{
		return reinterpret_cast<Bitmap*>(_image)->GetHeight();
	}
	unsigned ctlImage::getWidth() const
	{
		return reinterpret_cast<Bitmap*>(_image)->GetWidth();
	}
	bool ctlImage::isColor32() const
	{
		return reinterpret_cast<Bitmap*>(_image)->GetPixelFormat()
			== PixelFormat32bppARGB;
	}
	bool ctlImage::isColor24() const
	{
		return reinterpret_cast<Bitmap*>(_image)->GetPixelFormat()
			== PixelFormat24bppRGB;
	}
	void ctlImage::setColor(unsigned x, unsigned y, ctlColor color)
	{
		Bitmap* handle = reinterpret_cast<Bitmap*>(_image);
		if(isColor24() == false)
		{
			handle->SetPixel(x, y, MAKE_COLOR32(color));
		} else {
			handle->SetPixel(x, y, MAKE_COLOR24(color));
			SetPixel(_cpdc->GetDC(), x, y, MAKE_SYSTEM_RGB(color));
		}
	}
	ctlColor ctlImage::getColor(unsigned x, unsigned y)
	{
		Bitmap* handle = reinterpret_cast<Bitmap*>(_image);
		Color color;
		handle->GetPixel(x, y, &color);
		return color.GetValue();
	}
	void ctlImage::setColor(ctlPoint pt, ctlColor color)
	{
		setColor(pt.x, pt.y, color);
	}
	ctlColor ctlImage::getColor(ctlPoint pt)
	{
		return getColor(pt.x, pt.y);
	}
	int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
	{
		UINT num = 0;
		UINT size = 0;
		ImageCodecInfo* pImageCodecInfo = NULL;
		GetImageEncodersSize(&num, &size);
		if(size == 0)
			return -1;
		pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
		if(pImageCodecInfo == NULL)
			return -1;
		GetImageEncoders(num, size, pImageCodecInfo);
		for(UINT j = 0; j< num; ++j)
		{
			if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
			{
				*pClsid = pImageCodecInfo[j].Clsid;
				free(pImageCodecInfo);
				return j;
			}
		}
		free(pImageCodecInfo);
		return -1;
    }
	bool ctlImage::saveImage(const ctlText& filename, const ctlText& mimeFormat)
	{
		CLSID clsid;
		if(GetEncoderClsid(mimeFormat.toWideString().getWideString(), &clsid) == -1)
			return false;
		Bitmap *bmp = reinterpret_cast<Bitmap*>(_image);
		return bmp->Save(filename.toWideString().getWideString(), &clsid, NULL) == Ok;
	}
	bool ctlImage::isMatrixEnabled()
	{
		return _matrix_enable;
	}
	void ctlImage::enableMatrix(bool b)
	{
		_matrix_enable = b;
	}
}
